#include "stdafx.h"
#include "filesys/pipe.hpp"
#include "stdlib/bugcheck.hpp"
#include "stdlib/exception.hpp"
using namespace JHCPP::stdlib::jhException;

namespace JHCPP
{
	namespace filesys
	{
	#if defined(JH_OS_WINDOWS)
		CPipeImpl::CPipeImpl()
		{
			SECURITY_ATTRIBUTES attr;
			attr.nLength              = sizeof(attr);
			attr.lpSecurityDescriptor = NULL;
			attr.bInheritHandle       = FALSE;

			if (!::CreatePipe(&m_readHandle, &m_writeHandle, &attr, 0))
				throw CreateFileException("anonymous pipe");
		}

		CPipeImpl::~CPipeImpl()
		{
			closeRead();
			closeWrite();
		}

		int CPipeImpl::writeBytes(const void* buffer, int length)
		{
			jh_assert (m_writeHandle != INVALID_HANDLE_VALUE);

			DWORD bytesWritten = 0;
			if (!::WriteFile(m_writeHandle, buffer, length, &bytesWritten, NULL))
				throw WriteFileException("anonymous pipe");
			return bytesWritten;
		}

		int CPipeImpl::readBytes(void* buffer, int length)
		{
			jh_assert (m_readHandle != INVALID_HANDLE_VALUE);

			DWORD bytesRead = 0;
			BOOL ok = ::ReadFile(m_readHandle, buffer, length, &bytesRead, NULL);
			if (ok || GetLastError() == ERROR_BROKEN_PIPE)
				return bytesRead;
			else
				throw ReadFileException("anonymous pipe");
		}

		CPipeImpl::Handle CPipeImpl::readHandle() const
		{
			return m_readHandle;
		}

		CPipeImpl::Handle CPipeImpl::writeHandle() const
		{
			return m_writeHandle;
		}

		void CPipeImpl::closeRead()
		{
			if (m_readHandle != INVALID_HANDLE_VALUE)
			{
				::CloseHandle(m_readHandle);
				m_readHandle = INVALID_HANDLE_VALUE;
			}
		}

		void CPipeImpl::closeWrite()
		{
			if (m_writeHandle != INVALID_HANDLE_VALUE)
			{
				::CloseHandle(m_writeHandle);
				m_writeHandle = INVALID_HANDLE_VALUE;
			}
		}

	#elif defined(JH_OS_LINUX)
		CPipeImpl::CPipeImpl()
		{
			int fds[2];
			int rc = pipe(fds);
			if (rc == 0)
			{
				m_readfd  = fds[0];
				m_writefd = fds[1];
			}
			else throw CreateFileException("anonymous pipe");
		}

		CPipeImpl::~CPipeImpl()
		{
			closeRead();
			closeWrite();
		}

		int CPipeImpl::writeBytes(const void* buffer, int length)
		{
			jh_assert (m_writefd != -1);

			int n;
			do
			{
				n = write(m_writefd, buffer, length);
			}
			while (n < 0 && errno == EINTR);
			if (n >= 0)
				return n;
			else
				throw WriteFileException("anonymous pipe");
		}

		int CPipeImpl::readBytes(void* buffer, int length)
		{
			jh_assert (m_readfd != -1);

			int n;
			do
			{
				n = read(m_readfd, buffer, length);
			}
			while (n < 0 && errno == EINTR);
			if (n >= 0)
				return n;
			else
				throw ReadFileException("anonymous pipe");
		}

		CPipeImpl::Handle CPipeImpl::readHandle() const
		{
			return m_readfd;
		}

		CPipeImpl::Handle CPipeImpl::writeHandle() const
		{
			return m_writefd;
		}

		void CPipeImpl::closeRead()
		{
			if (m_readfd != -1)
			{
				close(m_readfd);
				m_readfd = -1;
			}
		}

		void CPipeImpl::closeWrite()
		{
			if (m_writefd != -1)
			{
				close(m_writefd);
				m_writefd = -1;
			}
		}
	#endif

		CPipe::CPipe() : m_pImpl(new CPipeImpl)
		{
		}

		CPipe::CPipe(const CPipe& pipe) : m_pImpl(pipe.m_pImpl)
		{
			m_pImpl->add();
		}

		CPipe::~CPipe()
		{
			m_pImpl->release();
		}

		CPipe& CPipe::operator = (const CPipe& pipe)
		{
			if (this != &pipe)
			{
				m_pImpl->release();
				m_pImpl = pipe.m_pImpl;
				m_pImpl->add();
			}
			return *this;
		}

		int CPipe::writeBytes(const void* buffer, int length)
		{
			return m_pImpl->writeBytes(buffer, length);
		}

		int CPipe::readBytes(void* buffer, int length)
		{
			return m_pImpl->readBytes(buffer, length);
		}

		CPipe::Handle CPipe::readHandle() const
		{
			return m_pImpl->readHandle();
		}

		CPipe::Handle CPipe::writeHandle() const
		{
			return m_pImpl->writeHandle();
		}

		void CPipe::close(CloseMode mode)
		{
			switch (mode)
			{
			case CLOSE_READ:
				m_pImpl->closeRead();
				break;
			case CLOSE_WRITE:
				m_pImpl->closeWrite();
				break;
			default:
				m_pImpl->closeRead();
				m_pImpl->closeWrite();
				break;
			}
		}
	}//end of namespace filesys
}//end of namespace JHCPP
